package com.example.config.mybatisPlus;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.plugins.InterceptorIgnoreHelper;
import com.baomidou.mybatisplus.core.toolkit.PluginUtils;
import com.baomidou.mybatisplus.core.toolkit.TableNameParser;
import com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.apache.ibatis.binding.MapperMethod;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;

import java.sql.Connection;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

/**
 * @author kedaji
 */

@NoArgsConstructor
@AllArgsConstructor
@Data
public class MyDynamicTableNameInterceptor implements InnerInterceptor {
    private Map<String, MyTableNameHandler> tableNameHandlerMap = new LinkedHashMap<>();
    private boolean useUnityRule = false;

    @Override
    public void beforeQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
        PluginUtils.MPBoundSql mpBs = PluginUtils.mpBoundSql(boundSql);
        if (InterceptorIgnoreHelper.willIgnoreDynamicTableName(ms.getId())) {
            return;
        }
        //mpBs.sql() 需要进行处理的sql，也就是写在xml文件中的sql，mpBs是一个重载方法
        mpBs.sql(this.changeTable(mpBs.sql(), parameter));
    }

    @Override
    public void beforePrepare(StatementHandler sh, Connection connection, Integer transactionTimeout) {
        PluginUtils.MPStatementHandler mpSh = PluginUtils.mpStatementHandler(sh);
        MappedStatement ms = mpSh.mappedStatement();
        SqlCommandType sct = ms.getSqlCommandType();
        if (sct == SqlCommandType.INSERT || sct == SqlCommandType.UPDATE || sct == SqlCommandType.DELETE) {
            if (InterceptorIgnoreHelper.willIgnoreDynamicTableName(ms.getId())) {
                return;
            }
            PluginUtils.MPBoundSql mpBs = mpSh.mPBoundSql();
            mpBs.sql(this.changeTable(mpBs.sql(), sh.getBoundSql().getParameterObject()));
        }
    }


    protected String changeTable(String sql, Object parameter) {
        // 剔除  sql中的 \r\n、把多个空格替换为一个空格，解析sql语句
        TableNameParser parser = new TableNameParser(sql);
        List<TableNameParser.SqlToken> names = new ArrayList<>();
        //根据指定关键词获取表名,根据关键字判断parser中的tokens中哪些可能是表名，关键词比如有 from, join、delete等
        parser.accept(names::add);
        StringBuilder builder = new StringBuilder();
        int last = 0;
        for (TableNameParser.SqlToken name : names) {
            int start = name.getStart();
            if (start != last) {
                builder.append(sql, last, start);
                String value = name.getValue();
                MyTableNameHandler handler = null;
                if (useUnityRule) {
                    handler = tableNameHandlerMap.get("*");
                } else {
                    handler = tableNameHandlerMap.get(value);
                }
                if (handler != null) {
                    // 处理
                    builder.append(handler.dynamicTableName(sql, value, getEntity(parameter)));
                } else {
                    builder.append(value);
                }
            }
            last = name.getEnd();
        }
        if (last != sql.length()) {
            //把表名之后的sql加上，比如 where order by 之类的
            builder.append(sql.substring(last));
        }
        return builder.toString();
    }

    //这里的动态表名的最终效果就是：如果想mapper层传了year参数（参数的名称也要是year），表名后加上 _year,如果没有向mapper层传year，默认取当前年份。

    private Object getEntity(Object parameter) {
        if (parameter == null) {
            return null;
        }
        if (parameter instanceof MapperMethod.ParamMap) {
            //一般来说传的参数都会被变成  MapperMethod.ParamMap,不管是实体、基本类型、QueryWrapper，
            // 都会被包装成 MapperMethod.ParamMap,除了特定的单个类型，比如说 string，int等
            // 值得注意的是 MybtaisPlus的QueryWrapper 被包装成键值对之后的值是ew
            //  MybatisPlus的update(Entity,UpdateWrapper) 则实体会被包装秤键值对之后的键为 et，wrapper是ew
            MapperMethod.ParamMap paramMap = (MapperMethod.ParamMap) parameter;

            //判断参数对应的key是否会被变成 ew
            if (!paramMap.containsKey("ew")) {
                return paramMap;
            }

            Object ew = paramMap.get("ew");

            //下面的是什么写法
            if (ew instanceof QueryWrapper) {
                QueryWrapper queryWrapper = (QueryWrapper) ew;
                return queryWrapper.getEntity();
            }
            if (ew instanceof UpdateWrapper) {
                UpdateWrapper updateWrapper = (UpdateWrapper) ew;
                return updateWrapper.getEntity();
            }

            Object et = paramMap.get("et");
            if (et != null) {
                return et;
            }
        }
        return parameter;
    }
}
